﻿using System;
using System.Xml;
using Notoric.Model.Internal;
using Notoric.Model.Packaging;

namespace Notoric.Model.Elements.CWNotation
{
    /// <summary>
    /// Represents a connector that has a shape of a cubic Bézier curve.
    /// </summary>
    public abstract class Arc : Connector
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Arc"/> class.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="XmlPackagePart"/>
        /// </para>
        /// <para>
        /// The package part that owns this connector.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="System"/>
        /// </para>
        /// <para>
        /// The system that directly owns this connector.
        /// </para>
        /// </param>
        /// <param name="presentedConnector">
        /// <para>
        /// Type: <see cref="ConnectorElement"/>
        /// </para>
        /// <para>
        /// The presented connector element.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// The <paramref name="owner"/> does not contain any of the symbols connected by the <paramref name="presentedConnector"/>.
        /// </exception>
        protected Arc(XmlPackagePart packagePart, System owner, ConnectorElement presentedConnector)
            : base(packagePart, owner, presentedConnector)
        {
            BezierPoint1 = new Point();
            BezierPoint2 = new Point();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Arc"/> class.
        /// </summary>
        /// <param name="packagePart">
        /// <para>
        /// Type: <see cref="XmlPackagePart"/>
        /// </para>
        /// <para>
        /// The package part that owns this connector.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="System"/>
        /// </para>
        /// <para>
        /// The system that directly owns this connector.
        /// </para>
        /// </param>
        /// <param name="element">
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The XML element that holds the data of this connector.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        internal Arc(XmlPackagePart packagePart, System owner, XmlElement element)
            : base(packagePart, owner, element)
        {
            point1Element = element[PackageVocabulary.BezierPoint1ElementName, PackageVocabulary.PackageNamespaceUri];
            point1 = new Point(point1Element.FirstChild as XmlElement);

            point2Element = element[PackageVocabulary.BezierPoint2ElementName, PackageVocabulary.PackageNamespaceUri];
            point2 = new Point(point2Element.FirstChild as XmlElement);
        }

        #endregion

        #region Connector Members

        /// <inheritdoc/>
        protected override XmlElement CreateConnectorElement(XmlDocument document)
        {
            var element = CreateArcElement(document);

            point1Element = document.CreateElement(
                PackageVocabulary.PackageNamespacePrefix,
                PackageVocabulary.BezierPoint1ElementName,
                PackageVocabulary.PackageNamespaceUri
                );
            point2Element = document.CreateElement(
                PackageVocabulary.PackageNamespacePrefix,
                PackageVocabulary.BezierPoint2ElementName,
                PackageVocabulary.PackageNamespaceUri
                );

            element.AppendChild(point1Element);
            element.AppendChild(point2Element);

            return element;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// When overriden in a derived class, this method creates the XML element
        /// that will hold the data of this arc.
        /// </summary>
        /// <remarks>
        /// The attributes and elements for inherited properties as well as the type information
        /// will be added by the base class.
        /// </remarks>
        /// <param name="document">
        /// <para>
        /// Type: <see cref="XmlDocument"/>
        /// </para>
        /// <para>
        /// The document that will own the created element.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The created element.
        /// </para>
        /// </returns>
        protected abstract XmlElement CreateArcElement(XmlDocument document);

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the first control point of the arc.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The first control point of the arc's cubic bezier curve.
        /// </para>
        /// </value>
        public Point BezierPoint1
        {
            get { return new Point(point1); }
            set
            {
                point1.SetOwner(null);
                point1 = value;
                point1.SetOwner(point1Element);

                RaisePropertyChanged("BezierPoint1");
            }
        }

        /// <summary>
        /// Gets or sets the second control point of the arc.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The second control point of the arc's cubic bezier curve.
        /// </para>
        /// </value>
        public Point BezierPoint2
        {
            get { return new Point(point2); }
            set
            {
                point2.SetOwner(null);
                point2 = value;
                point2.SetOwner(point2Element);

                RaisePropertyChanged("BezierPoint2");
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="BezierPoint1"/> property.
        /// </summary>
        private Point point1;
        /// <summary>
        /// Holds the value of the <see cref="BezierPoint2"/> property.
        /// </summary>
        private Point point2;
        /// <summary>
        /// Holds the value of the <see cref="BezierPoint1"/> property.
        /// </summary>
        private XmlElement point1Element;
        /// <summary>
        /// Holds the value of the <see cref="BezierPoint2"/> property.
        /// </summary>
        private XmlElement point2Element;

        #endregion
    }
}
